package com.onlyxiahui.app.fx;

import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.stage.Screen;
import javafx.stage.Stage;
//import javafx.stage.WindowEvent;
import javafx.util.Duration;

/**
 * 
 * Description <br>
 * Date 2020-06-19 11:32:39<br>
 * 
 * @author XiaHui [onlovexiahui@qq.com]<br>
 * @since 1.0.0
 */
public class OnlyWindowAutoHide {

	private Stage window;
	private DoubleProperty width = new SimpleDoubleProperty(0);
	private DoubleProperty height = new SimpleDoubleProperty(0);

	private DoubleProperty x = new SimpleDoubleProperty(0);
	private DoubleProperty y = new SimpleDoubleProperty(0);

	private BooleanProperty running = new SimpleBooleanProperty(false);

	private double w;
	private double h;

//	private double locationX;
//	private double locationY;

	private Timeline timeline = new Timeline();
	private Position position;
	private Position hidePosition = Position.TOP;
	boolean hide = false;

	boolean run = false;

	public OnlyWindowAutoHide(Stage window) {
		this.window = window;
		init();
		initWindow();
	}

	private void init() {
		timeline.setCycleCount(1);
		timeline.setAutoReverse(true);
		timeline.setOnFinished(new EventHandler<ActionEvent>() {

			@Override
			public void handle(ActionEvent event) {
				// TODO Auto-generated method stub
				run = false;
			}
		});
	}

	private void initWindow() {
		Scene scene = window.getScene();

		window.addEventFilter(MouseEvent.MOUSE_MOVED, mouseMoved);
		if (null != scene) {
			scene.addEventFilter(MouseEvent.MOUSE_EXITED_TARGET, mouseExited);
		} else {
			window.addEventFilter(MouseEvent.MOUSE_EXITED_TARGET, mouseExited);
		}
		window.addEventFilter(MouseEvent.MOUSE_ENTERED_TARGET, mouseEntered);

		window.addEventFilter(MouseEvent.MOUSE_DRAGGED, mouseDragged);

		window.focusedProperty().addListener(new ChangeListener<Boolean>() {

			@Override
			public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
				if (newValue) {
					show();
				} else {
					// hide();
				}
			}
		});

//		window.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() {
//		    @Override
//		    public void handle(MouseEvent mouseEvent) {
//		        System.out.println("mouse click detected! " + mouseEvent.getSource());
//		    }
//		});

//		window.addEventHandler(MouseEvent.MOUSE_MOVED, mouseMoved);
//		window.addEventHandler(MouseEvent.MOUSE_ENTERED, mouseEntered);
//		window.addEventHandler(MouseEvent.MOUSE_EXITED, mouseExited);
//		window.addEventHandler(MouseEvent.MOUSE_DRAGGED, mouseDragged);
		window.xProperty().addListener(new ChangeListener<Number>() {

			@Override
			public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
				// TODO Auto-generated method stub

			}

		});
		window.yProperty().addListener(new ChangeListener<Number>() {

			@Override
			public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
				// TODO Auto-generated method stub
			}
		});

		width.addListener(new ChangeListener<Number>() {

			@Override
			public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
				window.setWidth(newValue.doubleValue());
			}
		});

		height.addListener(new ChangeListener<Number>() {

			@Override
			public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
				window.setHeight(newValue.doubleValue());
			}
		});

		x.addListener(new ChangeListener<Number>() {

			@Override
			public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
				window.setX(newValue.doubleValue());
			}
		});

		y.addListener(new ChangeListener<Number>() {

			@Override
			public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
				window.setY(newValue.doubleValue());
			}
		});
	}

	EventHandler<MouseEvent> mouseMoved = new EventHandler<MouseEvent>() {

		@Override
		public void handle(MouseEvent e) {
//			if (isEdge(e, window)) {
//				// hide();
//			}
		}
	};

	EventHandler<MouseEvent> mouseEntered = new EventHandler<MouseEvent>() {

		@Override
		public void handle(MouseEvent e) {
			show();
		}
	};

	EventHandler<MouseEvent> mouseExited = new EventHandler<MouseEvent>() {

		@Override
		public void handle(MouseEvent e) {
//			if (!window.isMaximized()) {
//				System.out.println(e.getSource());
//				hide();
//			}

			if (!window.isMaximized()) {
				if (!isInside(window, e)) {
					hide();
				}
			}
		}
	};

	EventHandler<MouseEvent> mouseDragged = new EventHandler<MouseEvent>() {

		@Override
		public void handle(MouseEvent e) {

		}
	};

	public boolean isInside(Stage window, MouseEvent e) {
		boolean in = false;

		double width = window.getWidth();
		double height = window.getHeight();

		double x = e.getX();
		double y = e.getY();

		if ((0 < x && x < width) && (0 < y && y < height)) {
			in = true;
		}
		return in;
	}

	public void show() {
		if (hide && !run) {
			if (Position.TOP == position) {
				hide = false;
				run = true;
				check();
				showTop();
			} else if (Position.RIGHT == position) {
				hide = false;
				run = true;
				check();
				showRight();
			} else if (Position.BOTTOM == position) {
				hide = false;
				run = true;
				check();
				showBottom();
			} else if (Position.LEFT == position) {
				hide = false;
				run = true;
				check();
				showLeft();
			}
		}
	}

	public void showTop() {
		double nh = window.getHeight();
		KeyFrame from = new KeyFrame(Duration.ZERO, new KeyValue(height, nh));
		KeyFrame to = new KeyFrame(new Duration(500), new KeyValue(height, h));
		timeline.getKeyFrames().clear();
		timeline.getKeyFrames().add(from);
		timeline.getKeyFrames().add(to);
		timeline.play();
	}

	public void showRight() {

		Screen screen = Screen.getPrimary();
		Rectangle2D bounds = screen.getBounds();
		double sw = bounds.getWidth();

		double nw = window.getWidth();
		double nowX = window.getX();
		double toX = sw - nw;

		KeyFrame from = new KeyFrame(Duration.ZERO, new KeyValue(x, nowX));
		KeyFrame to = new KeyFrame(new Duration(500), new KeyValue(x, toX));
		timeline.getKeyFrames().clear();
		timeline.getKeyFrames().add(from);
		timeline.getKeyFrames().add(to);
		timeline.play();
	}

	public void showBottom() {

		Screen screen = Screen.getPrimary();
		Rectangle2D bounds = screen.getBounds();
		double sh = bounds.getHeight();

		double nh = window.getHeight();
		double nowY = window.getY();
		double toY = sh - nh;

		KeyFrame from = new KeyFrame(Duration.ZERO, new KeyValue(y, nowY));
		KeyFrame to = new KeyFrame(new Duration(500), new KeyValue(y, toY));
		timeline.getKeyFrames().clear();
		timeline.getKeyFrames().add(from);
		timeline.getKeyFrames().add(to);
		timeline.play();
	}

	public void showLeft() {
		// double x = window.getX();
		double toWidth = w;
		double nowWidth = window.getWidth();

		KeyFrame from = new KeyFrame(Duration.ZERO, new KeyValue(width, nowWidth));
		KeyFrame to = new KeyFrame(new Duration(500), new KeyValue(width, toWidth));
		timeline.getKeyFrames().clear();
		timeline.getKeyFrames().add(from);
		timeline.getKeyFrames().add(to);
		timeline.play();
	}

	public void hide() {
		if (!hide && !run) {
			checkSize();
			location();
			if (position == hidePosition) {
				startHide(hidePosition);
			} else if (null != hidePosition && hidePosition == Position.ALL) {
				startHide(position);
			}
		}
	}

	private void checkSize() {
		if (!isRunning() && !hide) {
			w = window.getWidth();
			h = window.getHeight();

//			locationX = window.getX();
//			locationY = window.getY();
		}
	}

	public void location() {
		Screen screen = Screen.getPrimary();
		Rectangle2D bounds = screen.getBounds();
		double sw = bounds.getWidth();
		double sh = bounds.getHeight();

		double x = window.getX();
		double y = window.getY();

		if (y <= 0) {
			position = Position.TOP;
		} else if ((x + w) >= sw) {
			position = Position.RIGHT;
		} else if ((y + h) >= sh) {
			position = Position.BOTTOM;
		} else if ((x) <= 0) {
			position = Position.LEFT;
		} else {
			position = null;
		}
	}

	public void startHide(Position position) {

		if (Position.TOP == position) {
			hide = true;
			run = true;
			check();
			hideTop();
		} else if (Position.RIGHT == position) {
			hide = true;
			run = true;
			check();
			hideRight();
		} else if (Position.BOTTOM == position) {
			hide = true;
			run = true;
			check();
			hideBottom();
		} else if (Position.LEFT == position) {
			hide = true;
			run = true;
			check();
			hideLeft();
		}
	}

	public void hideTop() {
		double y = window.getY();
		double toHeight = (y < 0) ? (2D - y) : (2D);
		double nowHeight = window.getHeight();

		KeyFrame from = new KeyFrame(Duration.ZERO, new KeyValue(height, nowHeight));
		KeyFrame to = new KeyFrame(new Duration(500), new KeyValue(height, toHeight));
		timeline.getKeyFrames().clear();
		timeline.getKeyFrames().add(from);
		timeline.getKeyFrames().add(to);
		timeline.play();
	}

	public void hideRight() {
		Screen screen = Screen.getPrimary();
		Rectangle2D bounds = screen.getBounds();
		double sw = bounds.getWidth();

		double nowX = window.getX();
		double toX = sw - 2D;

		KeyFrame from = new KeyFrame(Duration.ZERO, new KeyValue(x, nowX));
		KeyFrame to = new KeyFrame(new Duration(500), new KeyValue(x, toX));
		timeline.getKeyFrames().clear();
		timeline.getKeyFrames().add(from);
		timeline.getKeyFrames().add(to);
		timeline.play();
	}

	public void hideBottom() {

		Screen screen = Screen.getPrimary();
		Rectangle2D bounds = screen.getBounds();
		double sh = bounds.getHeight();

		double nowY = window.getY();
		double toY = sh - 2D;

		KeyFrame from = new KeyFrame(Duration.ZERO, new KeyValue(y, nowY));
		KeyFrame to = new KeyFrame(new Duration(500), new KeyValue(y, toY));
		timeline.getKeyFrames().clear();
		timeline.getKeyFrames().add(from);
		timeline.getKeyFrames().add(to);
		timeline.play();
	}

	public void hideLeft() {
		double x = window.getX();
		double toWidth = (x < 0) ? (2D - x) : (2D);
		double nowWidth = window.getWidth();

		KeyFrame from = new KeyFrame(Duration.ZERO, new KeyValue(width, nowWidth));
		KeyFrame to = new KeyFrame(new Duration(500), new KeyValue(width, toWidth));
		timeline.getKeyFrames().clear();
		timeline.getKeyFrames().add(from);
		timeline.getKeyFrames().add(to);
		timeline.play();
	}

	public void check() {
		if (timeline.getStatus() == Timeline.Status.RUNNING) {
			timeline.pause();
		}
	}

	public boolean isRunning() {
		return running.getValue();
	}

	public void setRunning(boolean running) {
		this.running.setValue(running);
	}

	public void setHidePosition(Position hidePosition) {
		this.hidePosition = hidePosition;
	}

	public static enum Position {

		LEFT, RIGHT, TOP, BOTTOM, ALL,
	}

//	private boolean isEdge(MouseEvent me, Window stage) {
//		boolean edge = false;
//
//		double width = stage.getWidth();
//		double height = stage.getHeight();
//
//		double x = me.getX();
//		double y = me.getY();
//
//		if (x <= 1 || y <= 1) {
//			edge = true;
//		} else if (x >= (width - 2) || y >= (height - 2)) {
//			edge = true;
//		} else {
//			edge = false;
//		}
//		// System.out.println(edge + "/width:" + width + "/x:" + x);
//		return edge;
//	}
}
